#region Copyright information
/* Copyright information
 * 
 * FileName:   	FormBase.cs
 * DateCreated: 03-31-2007
 * Author:     	Rader
 * Email:      	mailto:rader.net@gmail.com
 * Website:		http://raderdotnet.spaces.live.com
 
 * Copyright:	This file and the codes included are owned by Rader.
				You can use or distribute this file without removing the copyright information or modifying the cotent.
				The codes included can't be used with commecial purposes,unless you have got the perssion.
*/
#endregion

using System;
using System.Collections;
using System.Reflection;
using System.Windows.Forms;

namespace RaderDotNet.ValidationAttributes
{
    public  class ValidatorEngine
    {
        public ValidatorEngine() {
            reflectionCache = new Hashtable();
            defaultFlags = BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
        }
        
        /// <summary>
        /// return an array of <see cref="MemberInfo"/> which has been assgined with an attribute of the given type or its derived attribute
        /// </summary>
        /// <param name="attributeType">the type of an attribute,or the type of its super attribute</param> 
        /// <param name="hostType">the one contains memberinfos will be checked</param>
        /// <returns></returns>
        /// <remarks>if you want to get the members which have been defined with the exact attribute,please </remarks>
        public MemberInfo[] GetMembersAttributed(Type attributeType, Type hostType, BindingFlags flags)
        {
            MemberInfo[] members = hostType.FindMembers(MemberTypes.Custom | MemberTypes.Field | MemberTypes.Property, flags, new MemberFilter(IsDefined), attributeType);

            return members;
        }
        public MemberInfo[] GetMembersAttributed(Type attributeType, Type hostType)
        {
            return GetMembersAttributed(attributeType, hostType, defaultFlags);
        }        
        /// <summary>
        /// check if a given <see cref="MemberInfo">member</see> has an <see cref="Attribute">attribute</see> or an derived attribute of a given type;
        /// </summary>
        /// <param name="mi">MemberInfo to be checked</param>
        /// <param name="attributeType">an instance of the type of a attribute </param>
        /// <returns></returns>
        public bool IsDefined(MemberInfo mi, object attributeType)
        {           
            bool isDefined = (Attribute.IsDefined(mi, (Type)attributeType, false));
            return isDefined;
        }
        /// <summary>
        /// return an array of <see cref="MemberInfo"/> of a type with the given <see cref="BindingFlags"/>
        /// </summary>
        /// <param name="hostType">type of the container </param>
        /// <param name="flags"></param>
        /// <returns></returns>
        public MemberInfo[] GetFields(Type hostType, BindingFlags flags)
        {
            return hostType.GetFields(flags);
        }
        /// <summary>
        /// return an array of <see cref="FieldInfo"/> of a type with the given <see cref="BindingFlags"/>,
        /// and filted by if the field has been assgined with an attribute of the given type or its derived attribute
        /// </summary>
        /// <param name="hostType"></param>
        /// <param name="flags"></param>
        /// <param name="attributeType">type of an attribute</param>
        /// <returns></returns>
        public MemberInfo[] GetFields(Type hostType, BindingFlags flags, Type attributeType)
        {
            MemberInfo[] fields = hostType.FindMembers(MemberTypes.Field, flags, new MemberFilter(IsDefined), attributeType);
            return fields;
        }
        /// <summary>
        /// process attributes attached to the subcontrols(defined a fileds) of the host(container) control,
        /// register validating delegates of subcontrols with the ones defined in their attributes
        /// </summary>
        /// <param name="host">a container control wich contains the controls we want to validate,for example,form,user control,custom control...</param>
        /// <param name="flags"></param>
        public void Register(Control host,BindingFlags flags)
        {
            Register(host, flags, Type.Missing.GetType());
        }
        /// <summary>
        /// process attributes attached to the subcontrols(defined a fileds) of the host(container) control,
        /// register validating delegates of subcontrols with the ones defined in their attributes
        /// </summary>
        /// <param name="host">a container control wich contains the controls we want to validate,for example,form,user control,custom control...</param>
        /// <param name="flags"></param>
        /// <param name="attributeType"></param>
        public void Register(Control host, BindingFlags flags,Type attributeType)
        {
            MemberInfo[] fields;

            Type hostType = host.GetType();
            //if a given type has not been cached
            if (!reflectionCache.ContainsKey(hostType))
            {
                if(attributeType==Type.Missing.GetType())
                    reflectionCache.Add(hostType, GetFields(hostType, flags));
                else
                    reflectionCache.Add(hostType, GetFields(hostType, flags, attributeType));
            }
            fields = (MemberInfo[])reflectionCache[hostType];
            //
            RegisterControlValidatingEvents(host, fields);
        }
        private void RegisterControlValidatingEvents(Control container, MemberInfo[] fields)
        {
            foreach (MemberInfo fi in fields)
	        {
                //don't return the attributes derived
                object[] attrs = fi.GetCustomAttributes(false);
                Control ctr;
                if (attrs.Length > 0)
                {
                    //number of controls found will be always "1"
                    ctr = container.Controls.Find(fi.Name, true)[0];
                    
                    foreach (object att in attrs)
                    {
                        BaseCustomValidatorAttribute bcva = att as BaseCustomValidatorAttribute;
                        if (bcva != null)
                            //ctr.Validating += (CancelEventHandler)Delegate.CreateDelegate(typeof(CancelEventHandler), bcva.ValidatingDelegate.Method, true);
                            ctr.Validating += bcva.ValidatingDelegate;
                    }

                }
                
               
	        }
            
        }       
        
        Hashtable reflectionCache;
        BindingFlags defaultFlags;
    }
}
